package com.ly.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ly.common.core.bean.R;
import com.ly.common.core.bean.dto.EnableDTO;
import com.ly.common.core.constant.BaseCacheConstant;
import com.ly.common.core.constant.BaseConstant;
import com.ly.common.core.constant.TokenConstants;
import com.ly.common.core.exception.ServiceException;
import com.ly.common.core.utils.BaseUtils;
import com.ly.common.core.utils.CommonUtils;
import com.ly.common.core.utils.RedisUtils;
import com.ly.system.mapper.*;
import com.ly.system.service.*;
import com.ly.system.system.bean.dto.AppDTO;
import com.ly.system.system.bean.dto.LoginDTO;
import com.ly.system.system.bean.entity.*;
import com.ly.system.system.bean.model.UserDeptVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 用户-服务
 *
 * @author jeecg
 * @Date: 2018-12-20
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    private final ISysUserPositionService sysUserPositionService;
    private final ISysUserRoleService sysUserRoleService;
    private final ISysDeptService sysDepartService;
    private final ISysPostService sysPostService;

    private final SysUserMapper userMapper;
    private final SysUserRoleMapper sysUserRoleMapper;
    private final SysDeptMapper sysDeptMapper;
    private final SysRoleMapper sysRoleMapper;
    private final SysUserPositionMapper sysUserPositionMapper;
    private final RedisUtils redisUtils;

    @Override
    public R getId(String id) {
        SysUser entity = this.getById(id);
        if (entity == null) {
            return R.fail("未找到对应数据");
        }
        return R.ok(entity);
    }

    @Override
    public R getList(SysUser entity) {
        List<SysUser> list = this.list(
                new LambdaQueryWrapper<SysUser>()
                        .eq(StrUtil.isNotEmpty(entity.getOrgCode()), SysUser::getOrgCode, entity.getOrgCode())
                        .orderByAsc(SysUser::getOrgCode)
        );
        return R.ok(list);
    }

    @Override
    public R getPage(SysUser entity, Page<SysUser> page) {
        IPage<SysUser> pageList = this.page(
                page,
                new LambdaQueryWrapper<SysUser>()
                        .in(StrUtil.isNotEmpty(entity.getDeptId()), SysUser::getDeptId, StrUtil.split(entity.getDeptId(), ","))
                        .eq(StrUtil.isNotEmpty(entity.getOrgCode()), SysUser::getOrgCode, entity.getOrgCode())
                        .like(StrUtil.isNotEmpty(entity.getUsername()), SysUser::getUsername, entity.getUsername())
                        .like(StrUtil.isNotEmpty(entity.getRealname()), SysUser::getRealname, entity.getRealname())
                        .eq(StrUtil.isNotEmpty(entity.getPhone()), SysUser::getPhone, entity.getPhone())
                        .eq(ObjUtil.isNotNull(entity.getSex()), SysUser::getSex, entity.getSex())
                        .eq(ObjUtil.isNotNull(entity.getStatus()), SysUser::getStatus, entity.getStatus())
                        .eq(ObjUtil.isNotNull(entity.getTenantId()), SysUser::getTenantId, entity.getTenantId())
                        .orderByDesc(SysUser::getCreateTime)
        );
        // IPage<SysUser> pageList = userMapper.getUserPage(page, entity);
        List<SysUser> userList = pageList.getRecords();
        this.wrapperDeptNamesByUser(userList);
        return R.ok(pageList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R add(SysUser entity) {
        // 填充用户信息
        String salt = RandomUtil.randomString(TokenConstants.BASE_STR, 8);
        entity.setSalt(salt);
        String passwordEncode = CommonUtils.getAesPassWord(entity.getUsername(), entity.getPassword(), salt);
        entity.setPassword(passwordEncode);
        entity.setStatus(1);
        entity.setDelFlag(BaseConstant.DEL_FLAG_0);
        // 设置负责部门文本
        String deptIds = entity.getDeptIds();
        if (StrUtil.isNotEmpty(deptIds)) {
            List<String> deptIdList = Arrays.asList(deptIds.split(","));
            SysDept sysDept = new SysDept();
            sysDept.setIdList(deptIdList);
            R res = sysDepartService.getList(sysDept);
            List<SysDept> deptList = (List<SysDept>) res.getResult();
            String deptNames = deptList.stream().map(SysDept::getOrgName).collect(Collectors.joining(", "));
            entity.setDeptIdsText(deptNames);
        }
        boolean flag = this.save(entity);
        // 保存角色
        String selectedRoles = entity.getSelectedroles();
        if (StrUtil.isNotEmpty(selectedRoles)) {
            String[] arr = selectedRoles.split(",");
            for (String roleId : arr) {
                SysUserRole userRole = new SysUserRole(entity.getId(), roleId);
                sysUserRoleMapper.insert(userRole);
            }
        }
        // 保存职务信息
        String post = entity.getPost();
        if (StrUtil.isNotEmpty(post)) {
            String[] arr = post.split(",");
            for (String positionId : arr) {
                SysUserPosition userPosition = new SysUserPosition(entity.getId(), positionId);
                sysUserPositionMapper.insert(userPosition);
            }
        }
        return flag ? R.ok(true, "创建成功") : R.fail("创建失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {BaseCacheConstant.SYS_USERS_CACHE}, allEntries = true)
    public R edit(SysUser entity) {
        // 保护部门信息
        String id = entity.getId();
        // 保存角色
        String selectedRoles = entity.getSelectedroles();
        sysUserRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, id));
        if (StrUtil.isNotEmpty(selectedRoles)) {
            String[] arr = selectedRoles.split(",");
            for (String roleId : arr) {
                SysUserRole userRole = new SysUserRole(id, roleId);
                sysUserRoleMapper.insert(userRole);
            }
        }
        // 保存职务信息
        String post = entity.getPost();
        sysUserPositionMapper.delete(new LambdaQueryWrapper<SysUserPosition>().eq(SysUserPosition::getUserId, id));
        if (StrUtil.isNotEmpty(post)) {
            String[] arr = post.split(",");
            for (String positionId : arr) {
                SysUserPosition userPosition = new SysUserPosition(id, positionId);
                sysUserPositionMapper.insert(userPosition);
            }
        }
        // 设置负责部门文本
        String deptIds = entity.getDeptIds();
        if (StrUtil.isNotEmpty(deptIds)) {
            List<String> deptIdList = Arrays.asList(deptIds.split(","));
            SysDept sysDept = new SysDept();
            sysDept.setIdList(deptIdList);
            R res = sysDepartService.getList(sysDept);
            List<SysDept> deptList = (List<SysDept>) res.getResult();
            String deptNames = deptList.stream().map(SysDept::getOrgName).collect(Collectors.joining(", "));
            entity.setDeptIdsText(deptNames);
        } else {
            entity.setDeptIdsText("");
        }
        boolean flag = this.updateById(entity);
        return flag ? R.ok(true, "修改成功") : R.fail("修改失败");
    }

    @Override
    @CacheEvict(value = {BaseCacheConstant.SYS_USERS_CACHE}, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public R delete(String id) {
        this.checkUserAdminRejectDel(id);
        boolean flag = this.removeById(id);
        return flag ? R.ok(true, "删除成功") : R.fail("删除失败");
    }

    @Override
    @CacheEvict(value = {BaseCacheConstant.SYS_USERS_CACHE}, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public R deleteBatch(String ids) {
        this.checkUserAdminRejectDel(ids);
        boolean flag = this.removeByIds(Arrays.asList(ids.split(",")));
        return flag ? R.ok(true, "批量删除成功") : R.fail("批量删除失败");
    }

    @Override
    public R getCurrentUserData() {
        String userId = BaseUtils.getId();
        SysUser user = this.getById(userId);
        if (user == null) {
            return R.fail("未找到该用户数据");
        }
        List<SysPost> sysPostList = sysPostService.getPositionList(userId);
        String ids = sysPostList.stream().map(n -> n.getId()).collect(Collectors.joining(","));
        String names = sysPostList.stream().map(n -> n.getName()).collect(Collectors.joining(","));
        user.setPost(ids);
        user.setPostText(names);
        return R.ok(user);
    }

    @Override
    public R getRoleByUserId(String userid) {
        List<SysUserRole> userRoleList = sysUserRoleService.list(
                new LambdaQueryWrapper<SysUserRole>()
                        .eq(SysUserRole::getUserId, userid)
        );
        List<String> list = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
        return R.ok(list);
    }

    @Override
    public R getPostByUserId(String userid) {
        List<SysUserPosition> userRoleList = sysUserPositionService.list(
                new LambdaQueryWrapper<SysUserPosition>()
                        .eq(SysUserPosition::getUserId, userid)
        );
        List<String> list = userRoleList.stream().map(SysUserPosition::getPositionId).collect(Collectors.toList());
        return R.ok(list);
    }

    @Override
    public R updatePassword4App(AppDTO dto) {
        String username = dto.getUsername();
        String smscode = dto.getSmscode();
        String password = dto.getPassword();
        String phone = dto.getPhone();
        if (StrUtil.isEmpty(username) || StrUtil.isEmpty(password) || StrUtil.isEmpty(smscode) || StrUtil.isEmpty(phone)) {
            return R.fail("重置密码失败！");
        }
        String redisKey = BaseConstant.PHONE_REDIS_KEY_PRE + phone;
        Object object = redisUtils.get(redisKey);
        if (null == object) {
            return R.fail("短信验证码已过期！");
        }
        if (!smscode.equals(object.toString())) {
            return R.fail("短信验证码不匹配！");
        }
        SysUser sysUser = this.getOne(
                new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername, username)
                        .eq(SysUser::getPhone, phone)
        );
        if (sysUser == null) {
            return R.fail("当前登录用户和绑定的手机号不匹配，无法修改密码！");
        }
        String salt = RandomUtil.randomString(TokenConstants.BASE_STR, 8);
        sysUser.setSalt(salt);
        String passwordEncode = CommonUtils.getAesPassWord(sysUser.getUsername(), password, salt);
        sysUser.setPassword(passwordEncode);
        this.updateById(sysUser);
        // 修改完密码后清空redis
        redisUtils.removeAll(redisKey);
        return R.ok("密码重置完成！");
    }

    @Override
    @CacheEvict(value = {BaseCacheConstant.SYS_USERS_CACHE}, allEntries = true)
    public R updatePassword(LoginDTO dto) {
        String username = dto.getUsername();
        String oldPassword = dto.getOldPassword();
        String password = dto.getPassword();
        String confirmPassword = dto.getConfirmPassword();
        if (!BaseUtils.getUsername().equals(username) && !BaseUtils.checkIsAdmin()) {
            return R.fail("只允许修改自己的密码！");
        }
        SysUser user = this.getUserByName(username);
        if (user == null) {
            return R.fail("用户不存在！");
        }
        String passwordEncode = CommonUtils.getAesPassWord(username, oldPassword, user.getSalt());
        if (!user.getPassword().equals(passwordEncode)) {
            return R.fail("旧密码输入错误!");
        }
        if (StrUtil.isEmpty(password)) {
            return R.fail("新密码不允许为空!");
        }
        if (!password.equals(confirmPassword)) {
            return R.fail("两次输入密码不一致!");
        }
        String finalPassword = CommonUtils.getAesPassWord(username, password, user.getSalt());
        this.update(
                new LambdaUpdateWrapper<SysUser>()
                        .set(SysUser::getPassword, finalPassword)
                        .eq(SysUser::getId, user.getId())
        );
        return R.ok("密码重置成功!");
    }

    @Override
    public SysUser getUserByName(String username) {

        SysUser sysUser = this.getOne(
                new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername,username)
        );
        return sysUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUserWithRole(SysUser user, String roles) {
        this.save(user);
        if (StrUtil.isNotEmpty(roles)) {
            String[] arr = roles.split(",");
            for (String roleId : arr) {
                SysUserRole userRole = new SysUserRole(user.getId(), roleId);
                sysUserRoleMapper.insert(userRole);
            }
        }
    }

    public void wrapperDeptNamesByUser(List<SysUser> list) {
        List<String> deptIds = list.stream()
                .filter(n -> StrUtil.isNotEmpty(n.getDeptId())).map(SysUser::getDeptId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(deptIds)) {
            List<SysDept> deptList = sysDepartService.list(
                    new LambdaQueryWrapper<SysDept>()
                            .in(SysDept::getId, deptIds)
            );
            // 分组，拼接
            Map<String, String> deptMap = deptList.stream().collect(Collectors.groupingBy(
                    SysDept::getId,
                    Collectors.mapping(
                            SysDept::getOrgName,
                            Collectors.collectingAndThen(Collectors.toSet(), set -> String.join(", ", set))
                    )
            ));
            // 设置部门信息
            list.forEach(item -> item.setOrgCodeText(deptMap.get(item.getDeptId())));
        }
    }

    @Override
    public R getUserByDept(String id, IPage page) {
        if (StrUtil.isEmpty(id)) {
            return R.ok();
        }
        List<UserDeptVO> list = userMapper.getUserByDept(page, id);
        IPage<UserDeptVO> result = new Page<>(page.getCurrent(), page.getSize());
        result.setRecords(list);
        // todo: total未处理 result.setTotal()
        return R.ok(result);
    }

    /**
     * 根据角色Id查询
     *
     * @param page
     * @param roleId   角色id
     * @param username 用户账户名称
     * @return
     */
    @Override
    public IPage<SysUser> getUserByRoleId(Page<SysUser> page, String roleId, String username) {
        IPage<SysUser> pageList = userMapper.getUserByRoleId(page, roleId, username);
        List<SysUser> userList = pageList.getRecords();
        this.wrapperDeptNamesByUser(userList);
        return pageList;
    }

    @Override
    public SysUser getUserByPhone(String phone) {
        return userMapper.getUserByPhone(phone);
    }

    @Override
    public SysUser getUserByEmail(String email) {
        return userMapper.getUserByEmail(email);
    }

    /**
     * 校验用户是否有效
     *
     * @param sysUser
     * @return
     */
    @Override
    public R checkUserIsEffective(SysUser sysUser) {
        // 情况1：根据用户信息查询，该用户不存在
        if (sysUser == null) {
            return R.fail("该用户不存在，请注册");
        }
        // 情况2：根据用户信息查询，该用户已注销
        if (BaseConstant.DEL_FLAG_1.equals(sysUser.getDelFlag())) {
            // update-end---author:王帅   Date:20200601  for：if条件永远为falsebug------------
            return R.fail("该用户已注销");
        }
        // 情况3：根据用户信息查询，该用户已冻结
        if (BaseConstant.USER_FREEZE.equals(sysUser.getStatus())) {
            return R.fail("该用户已冻结");
        }
        return R.ok();
    }

    @Override
    public R getRecycleUser() {
        List<SysUser> userList = this.list(
                new LambdaQueryWrapper<SysUser>()
                        .ne(SysUser::getStatus, BaseConstant.USER_QUIT)
                        .eq(SysUser::getDelFlag, BaseConstant.DEL_FLAG_1)
        );
        return R.ok(userList);
    }

    @Override
    @CacheEvict(value = {BaseCacheConstant.SYS_USERS_CACHE}, allEntries = true)
    public R putRecycleUser(EnableDTO dto) {
        String ids = dto.getIds();
        boolean update = this.update(
                new LambdaUpdateWrapper<SysUser>()
                        .in(SysUser::getId, Arrays.asList(ids.split(",")))
                        .set(SysUser::getStatus, BaseConstant.USER_UNFREEZE)
        );
        return update ? R.ok(true, "操作成功") : R.fail("操作失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R removeLogicDeleted(String userIds) {
        List<String> userIdList = Arrays.asList(userIds.split(","));
        // 1. 删除用户
        int line = userMapper.deleteLogicDeleted(userIdList);
        // 3. 删除用户角色关系
        line += sysUserRoleMapper.delete(
                new LambdaQueryWrapper<SysUserRole>()
                        .in(SysUserRole::getUserId, userIdList)
        );
        boolean update = line != 0;
        return update ? R.ok(true, "删除成功") : R.fail("删除失败");
    }

    /**
     * 修改账号状态
     */
    @Override
    @CacheEvict(value = {BaseCacheConstant.SYS_USERS_CACHE}, allEntries = true)
    public R updateStatus(EnableDTO dto) {
        String ids = dto.getIds();
        String status = dto.getStatus();
        if (StrUtil.isBlank(status)) {
            return R.fail(false, "转态不能为空");
        }
        boolean flag = this.update(
                new LambdaUpdateWrapper<SysUser>()
                        .set(SysUser::getStatus, status)
                        .in(SysUser::getId, Arrays.asList(ids.split(",")))
        );
        return flag ? R.ok(true, "修改成功") : R.fail(false, "修改失败");
    }

    @Override
    public void checkUserAdminRejectDel(String userIds) {
        Long adminRoleCount = this.count(
                new LambdaQueryWrapper<SysUser>()
                        .in(SysUser::getId, Arrays.asList(userIds.split(",")))
                        .eq(SysUser::getUsername, "admin")
        );
        // 大于0说明存在管理员用户，不允许删除
        if (adminRoleCount > 0) {
            throw new ServiceException("admin用户，不允许删除！");
        }
    }

    @Override
    public R changePhone(AppDTO dto) {
        String username = BaseUtils.getUsername();
        String type = dto.getType();
        String smscode = dto.getSmscode();
        String phone = dto.getPhone();
        if (StrUtil.isEmpty(phone)) {
            throw new ServiceException("请填写原手机号！");
        }
        if (StrUtil.isEmpty(smscode)) {
            throw new ServiceException("请填写验证码！");
        }
        // step1 验证原手机号是否和当前用户匹配
        SysUser sysUser = this.getOne(
                new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername, username)
                        .eq(SysUser::getPhone, phone)
        );
        if (null == sysUser) {
            throw new ServiceException("原手机号不匹配，无法修改密码！");
        }
        // step2 根据类型判断是验证原手机号的验证码还是新手机号的验证码
        // 验证原手机号
        if (BaseConstant.PHONE_ORIGINAL.equals(type)) {
            this.verifyPhone(phone, smscode);
        } else if (BaseConstant.PHONE_NEW.equals(type)) {
            String newPhone = dto.getNewPhone();
            if (phone.equals(newPhone)) {
                throw new ServiceException("新手机号与原手机号一致，无法修改！");
            }
            this.verifyPhone(newPhone, smscode);
            this.update(
                    new LambdaUpdateWrapper<SysUser>()
                            .eq(SysUser::getId, sysUser.getId())
                            .set(SysUser::getPhone, newPhone)
            );
        }
        return R.ok("修改手机号成功！");
    }

    /**
     * 验证手机号
     *
     * @param phone
     * @param smsCode
     * @return
     */
    public void verifyPhone(String phone, String smsCode) {
        String phoneKey = BaseConstant.CHANGE_PHONE_REDIS_KEY_PRE + phone;
        Object phoneCode = redisUtils.get(phoneKey);
        if (null == phoneCode) {
            throw new ServiceException("验证码失效，请重新发送验证码！");
        }
        if (!smsCode.equals(phoneCode.toString())) {
            throw new ServiceException("短信验证码不匹配！");
        }
        // 验证完成之后清空手机验证码
        redisUtils.removeAll(phoneKey);
    }
}
